package design.simdi.code.final_paper.core.services.impl;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import design.simdi.code.final_paper.core.daos.AbsolventDao;
import design.simdi.code.final_paper.core.daos.DomainDao;
import design.simdi.code.final_paper.core.daos.PermissionDao;
import design.simdi.code.final_paper.core.daos.PersonDao;
import design.simdi.code.final_paper.core.daos.ProfesorDao;
import design.simdi.code.final_paper.core.daos.RoleDao;
import design.simdi.code.final_paper.core.daos.RolePermissionDao;
import design.simdi.code.final_paper.core.daos.StudentDao;
import design.simdi.code.final_paper.core.daos.UserDao;
import design.simdi.code.final_paper.core.daos.UserRoleDao;
import design.simdi.code.final_paper.core.entities.Absolvent;
import design.simdi.code.final_paper.core.entities.Domain;
import design.simdi.code.final_paper.core.entities.Person;
import design.simdi.code.final_paper.core.entities.Profesor;
import design.simdi.code.final_paper.core.entities.Role;
import design.simdi.code.final_paper.core.entities.Student;
import design.simdi.code.final_paper.core.entities.User;
import design.simdi.code.final_paper.core.entities.UserRole;
import design.simdi.code.final_paper.core.enums.UserStatus;
import design.simdi.code.final_paper.core.queries.LicentaHQLQueries;
import design.simdi.code.final_paper.core.services.ServiceException;
import design.simdi.code.final_paper.core.services.UserService;
import design.simdi.code.final_paper.site.application.ApplicationRoles;
import design.simdi.code.final_paper.utils.CryptoUtils;

/**
 * 
 * @author Marius Dumitriu - (May 21, 2010 - 9:09:26 PM)
 * 
 */
@Transactional
@Service("userService")
public class UserServiceImpl implements UserService {
	@Autowired
	private UserDao userDao;
	@Autowired
	private StudentDao studentDao;
	@Autowired
	private ProfesorDao profesorDao;
	@Autowired
	private PersonDao personDao;
	@Autowired
	private PermissionDao permissionDao;
	@Autowired
	private RolePermissionDao rolePermissionDao;
	@Autowired
	private RoleDao rolesDao;
	@Autowired
	private UserRoleDao userRoleDao;
	@Autowired
	private DomainDao domainDao;
	@Autowired
	private AbsolventDao absolventDao;

	public void setAbsolventDao(AbsolventDao absolventDao) {
		this.absolventDao = absolventDao;
	}

	public AbsolventDao getAbsolventDao() {
		return absolventDao;
	}

	/**
	 * @return the permissionDao
	 */
	public PermissionDao getPermissionDao() {
		return permissionDao;
	}

	/**
	 * @param permissionDao
	 *            the permissionDao to set
	 */
	public void setPermissionDao(PermissionDao permissionDao) {
		this.permissionDao = permissionDao;
	}

	/**
	 * @return the rolePermissionDao
	 */
	public RolePermissionDao getRolePermissionDao() {
		return rolePermissionDao;
	}

	/**
	 * @param rolePermissionDao
	 *            the rolePermissionDao to set
	 */
	public void setRolePermissionDao(RolePermissionDao rolePermissionDao) {
		this.rolePermissionDao = rolePermissionDao;
	}

	/**
	 * @return the userRoleDao
	 */
	public UserRoleDao getUserRoleDao() {
		return userRoleDao;
	}

	/**
	 * @param userRoleDao
	 *            the userRoleDao to set
	 */
	public void setUserRoleDao(UserRoleDao userRoleDao) {
		this.userRoleDao = userRoleDao;
	}

	/**
	 * @param rolesDao
	 *            the rolesDao to set
	 */
	public void setRolesDao(RoleDao rolesDao) {
		this.rolesDao = rolesDao;
	}

	/**
	 * @return the studentDao
	 */
	public StudentDao getStudentDao() {
		return studentDao;
	}

	/**
	 * @param studentDao
	 *            the studentDao to set
	 */
	public void setStudentDao(StudentDao studentDao) {
		this.studentDao = studentDao;
	}

	/**
	 * @return the profesorDao
	 */
	public ProfesorDao getProfesorDao() {
		return profesorDao;
	}

	/**
	 * @param profesorDao
	 *            the profesorDao to set
	 */
	public void setProfesorDao(ProfesorDao profesorDao) {
		this.profesorDao = profesorDao;
	}

	/**
	 * @return the personDao
	 */
	public PersonDao getPersonDao() {
		return personDao;
	}

	/**
	 * @param personDao
	 *            the personDao to set
	 */
	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}

	/**
	 * @param userDao
	 *            the userDao to set
	 */

	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	/**
	 * @return the userDao
	 */
	public UserDao getUserDao() {
		return userDao;
	}

	public User findUserByNameAndPassword(String username, String password) {
		String query = String.format(LicentaHQLQueries.findUserByLoginAndPassword.getQuery(), username, password);

		List<User> lista = userDao.find(query);

		if (lista != null && lista.size() > 0)
			return lista.get(0);
		return null;
	}

	public User findById(Long id) {
		return userDao.get(id);
	}

	public void addUser(User u, Person p, Student s, Profesor prof, boolean assignRoles) throws ServiceException {
		// verific daca mai exista username-ul in baza de date
		User mock = new User();
		mock.setLogin(u.getLogin());
		List<User> listaUseriExistenti = userDao.findFiltered(mock);
		if (listaUseriExistenti != null && listaUseriExistenti.size() > 0)
			throw new ServiceException("error.login.existent");

		userDao.saveOrUpdate(u);

		p.setUser(u);

		// verific daca este rol de profesor si student adaugate in baza de date
		// daca nu sunt atunci le adaug eu acum.Ar trebui sa fie tot timpul.
		checkRoles();

		if (prof != null) {
			p.setProfesor(prof);
			profesorDao.saveOrUpdate(prof);
			if (assignRoles) {
				assignRole(u, ApplicationRoles.PROFESSOR);

			}
		}
		if (s != null) {
			p.setStudent(s);
			studentDao.saveOrUpdate(s);
			if (assignRoles) {
				assignRole(u, ApplicationRoles.STUDENT);

			}
		}
		personDao.saveOrUpdate(p);

	}

	public void assignRole(User u, String rol) {
		u = userDao.get(u.getId());

		if (u.getUserRoles() != null)
			for (UserRole userRolu : u.getUserRoles())
				if (userRolu.getRole().getName().equals(rol))
					return;

		UserRole ur = new UserRole();
		Role r = new Role();
		r.setName(rol);
		r = rolesDao.findFilteredFirst(r);
		ur.setRole(r);
		ur.setUser(u);
		List<UserRole> userRoles = new ArrayList<UserRole>();

		if (u.getUserRoles() != null)
			u.getUserRoles().add(ur);
		else {
			userRoles.add(ur);
			u.setUserRoles(userRoles);
		}

		userRoleDao.saveOrUpdate(ur);
		userDao.saveOrUpdate(u);

	}

	public void removeRole(User u, String rol) {
		u = userDao.get(u.getId());

		UserRole UR = null;
		for (UserRole ur : u.getUserRoles())
			if (ur.getRole().getName().equals(rol)) {
				UR = ur;
				userRoleDao.delete(ur.getId());
			}
		u.getUserRoles().remove(UR);
		userDao.saveOrUpdate(u);

	}

	public void checkRoles() {
		List<Role> listaRoluri = new ArrayList<Role>();
		String query = String
				.format(LicentaHQLQueries.findRoleByName.getQuery(), ApplicationRoles.PROFESSOR.toString());
		listaRoluri = rolesDao.find(query);
		if (!(listaRoluri != null && listaRoluri.size() > 0)) {
			Role r = new Role();
			r.setDescription("generated by app");
			r.setName(ApplicationRoles.PROFESSOR.toString());
			rolesDao.merge(r);
		}
		query = String.format(LicentaHQLQueries.findRoleByName.getQuery(), ApplicationRoles.STUDENT.toString());
		listaRoluri = rolesDao.find(query);
		if (!(listaRoluri != null && listaRoluri.size() > 0)) {
			Role r = new Role();
			r.setDescription("generated by app");
			r.setName(ApplicationRoles.STUDENT.toString());
			rolesDao.merge(r);
		}

		query = String.format(LicentaHQLQueries.findRoleByName.getQuery(), ApplicationRoles.ADMIN.toString());
		listaRoluri = rolesDao.find(query);
		if (!(listaRoluri != null && listaRoluri.size() > 0)) {
			Role r = new Role();
			r.setDescription("generated by app");
			r.setName(ApplicationRoles.ADMIN.toString());
			rolesDao.merge(r);
		}

		query = String.format(LicentaHQLQueries.findRoleByName.getQuery(), ApplicationRoles.ABSOLVENT.toString());
		listaRoluri = rolesDao.find(query);
		if (!(listaRoluri != null && listaRoluri.size() > 0)) {
			Role r = new Role();
			r.setDescription("generated by app");
			r.setName(ApplicationRoles.ABSOLVENT.toString());
			rolesDao.merge(r);
		}

	}

	public void registerUserProfesor(User u, Person p, Profesor prof) {

		// Set<Permission> roluri = new HashSet<Permission>();
		// String query = String
		// .format(LicentaHQLQueries.findRoleByName.getQuery(),
		// ApplicationRoles.PROFESSOR.toString());
		//
		// List<Permission> rolur = rolesDao.find(query);
		// if (rolur != null && rolur.size() > 0)
		// roluri.add(rolur.get(0));
		// else {
		// Permission r = new Permission();
		// r.setName(ApplicationRoles.PROFESSOR.toString());
		// // Set<Permission> permissions = new HashSet<Permission>();
		// // r.setPermissions(permissions);
		// r.setDescription("created by application");
		// r = rolesDao.merge(r);
		// roluri.add(r);
		// }
		//
		// // u.setRoles(roluri);
		// u = userDao.merge(u);
		// p = personDao.merge(p);
		// // prof.setUser(u);
		// prof.setPerson(p);
		// profesorDao.merge(prof);

	}

	public void registerUserStudent(User u, Person p, Student s) {

		// Set<Permission> roluri = new HashSet<Permission>();
		// String query =
		// String.format(LicentaHQLQueries.findRoleByName.getQuery(),
		// ApplicationRoles.STUDENT.toString());
		//
		// List<Permission> rolur = rolesDao.find(query);
		// if (rolur != null && rolur.size() > 0)
		// roluri.add(rolur.get(0));
		// else {
		// Permission r = new Permission();
		// r.setName(ApplicationRoles.STUDENT.toString());
		// // Set<Permission> permissions = new HashSet<Permission>();
		// // r.setPermissions(permissions);
		// r.setDescription("created by application");
		// r = rolesDao.merge(r);
		// roluri.add(r);
		// }
		// // u.setRoles(roluri);
		//
		// u = userDao.merge(u);
		// p = personDao.merge(p);
		// // s.setUser(u);
		// s.setPerson(p);
		// studentDao.merge(s);

	}

	public User findUserByLogin(String username) {
		User u = new User();
		u.setLogin(username);
		return userDao.findFilteredFirst(u);
	}

	public void initDataBase() {
		checkRoles();
		User userStudent = new User();
		User userProfessor = new User();
		User userAdmin = new User();
		User userAdminProfesor = new User();
		Person ps = new Person();
		Person pp = new Person();
		Person pa = new Person();
		Person pap = new Person();

		Profesor prof = new Profesor();
		Student stud = new Student();
		Profesor pprof = new Profesor();

		userStudent.setEmail("student@student.com");
		userStudent.setLogin("student");
		userStudent.setPassword(CryptoUtils.getHash("student"));

		userAdminProfesor.setEmail("admin@profesor.com");
		userAdminProfesor.setLogin("adminprofesor");
		userAdminProfesor.setPassword(CryptoUtils.getHash("adminprofesor"));

		userProfessor.setEmail("profesor@profesor.com");
		userProfessor.setLogin("profesor");
		userProfessor.setPassword(CryptoUtils.getHash("profesor"));

		userAdmin.setEmail("admin@admin.com");
		userAdmin.setLogin("admin");
		userAdmin.setPassword(CryptoUtils.getHash("admin"));

		userAdmin.setStatus(UserStatus.ACTIVE);
		userStudent.setStatus(UserStatus.ACTIVE);
		userProfessor.setStatus(UserStatus.ACTIVE);
		userAdminProfesor.setStatus(UserStatus.ACTIVE);

		userDao.saveOrUpdate(userAdmin);
		userDao.saveOrUpdate(userProfessor);
		userDao.saveOrUpdate(userStudent);
		userDao.saveOrUpdate(userAdminProfesor);

		stud.setAn(1);
		stud.setGrupa("RE");
		stud.setSemian("A");

		ps.setNume("Student");
		ps.setPrenume("Student");

		pa.setNume("Admin");
		pa.setPrenume("Admin");

		pp.setNume("Profesor");
		pp.setPrenume("Profesor");

		pap.setNume("Profesor");
		pap.setPrenume("Admin");

		ps.setUser(userStudent);
		pa.setUser(userAdmin);
		pp.setUser(userProfessor);
		pap.setUser(userAdminProfesor);

		pprof.setTitluUniversitar("titlu de admin");
		profesorDao.saveOrUpdate(pprof);

		prof.setTitluUniversitar("profesor");
		profesorDao.saveOrUpdate(prof);

		stud.setAn(3);
		stud.setNumarMatricol("1234567890");
		studentDao.saveOrUpdate(stud);

		ps.setStudent(stud);
		pp.setProfesor(prof);
		pap.setProfesor(pprof);

		personDao.saveOrUpdate(pa);
		personDao.saveOrUpdate(ps);
		personDao.saveOrUpdate(pp);
		personDao.saveOrUpdate(pap);

		assignRole(userAdmin, ApplicationRoles.ADMIN);
		assignRole(userProfessor, ApplicationRoles.PROFESSOR);
		assignRole(userStudent, ApplicationRoles.STUDENT);
		assignRole(userAdminProfesor, ApplicationRoles.PROFESSOR);
		assignRole(userAdminProfesor, ApplicationRoles.ADMIN);

	}

	public void setDomainDao(DomainDao domainDao) {
		this.domainDao = domainDao;
	}

	public DomainDao getDomainDao() {
		return domainDao;
	}

	public List<Domain> getAllDomains() {
		return domainDao.findAll();
	}

	public Person findPersonByUser(User user) {
		if (user == null)
			return null;
		List<Criterion> criterii = new ArrayList<Criterion>();
		criterii.add(Restrictions.eq("user.id", user.getId()));
		List<Person> lista = personDao.findAll(new Order[] {}, criterii);
		if (lista.size() == 0)
			return null;
		return lista.get(0);
	}

	public Profesor findProfesorByUser(User user) {
		Person pers = findPersonByUser(user);
		if (pers == null)
			return null;
		List<Criterion> criterii = new ArrayList<Criterion>();
		criterii.add(Restrictions.eq("person.id", pers.getId()));

		List<Profesor> lista = profesorDao.findAll(new Order[] {}, criterii);
		if (lista.size() == 0)
			return null;
		return lista.get(0);
	}

	public Student findStudentByUser(User user) {
		// Person pers = findPersonByUser(user);
		// if (pers == null)
		// return null;
		// List<Criterion> criterii = new ArrayList<Criterion>();
		// criterii.add(Restrictions.eq("person.id", pers.getId()));
		//
		// List<Person> lista = personDao.findAll(new Order[] {}, criterii);
		// if (lista.size() == 0)
		return null;
		// return lista.get(0).getStudent();
	}

	public void addProfesorUser(Person professor) throws ServiceException {
		User u = professor.getUser();
		Profesor p = professor.getProfesor();

		if (u.getId() == null) {
			// verific daca mai exista user-ul
			User mock = new User();
			mock.setLogin(u.getLogin());
			List<User> listaUseriExistenti = userDao.findFiltered(mock);
			if (listaUseriExistenti != null && listaUseriExistenti.size() > 0)
				throw new ServiceException("error.login.existent");
		}
		if (u.getId() == null) {
			u.setStatus(UserStatus.NEVER_USED);
			u.setPassword(CryptoUtils.getHash(u.getPassword()));
		}

		userDao.saveOrUpdate(u);

		profesorDao.saveOrUpdate(p);

		if (professor.getId() == null)
			assignRole(u, ApplicationRoles.PROFESSOR);

		personDao.saveOrUpdate(professor);

	}

	public void removeProfesorUser(Person professor) {
		User u = professor.getUser();

		u = userDao.get(u.getId());

		if (u.getStatus().equals(UserStatus.NEVER_USED)) {
			profesorDao.delete(professor.getProfesor().getId());
			for (UserRole ur : u.getUserRoles())
				userRoleDao.delete(ur.getId());
			userDao.delete(u.getId());
			personDao.delete(professor.getId());
			return;
		}

		u.setStatus(UserStatus.MARK_FOR_DELETE);

		userDao.saveOrUpdate(u);

	}

	public void addStudentUser(Person student) throws ServiceException {
		User u = student.getUser();
		Student p = student.getStudent();

		if (u.getId() == null) {
			// verific daca mai exista user-ul
			User mock = new User();
			mock.setLogin(u.getLogin());
			List<User> listaUseriExistenti = userDao.findFiltered(mock);
			if (listaUseriExistenti != null && listaUseriExistenti.size() > 0)
				throw new ServiceException("error.login.existent");
		}
		if (u.getId() == null) {
			u.setStatus(UserStatus.NEVER_USED);
			u.setPassword(CryptoUtils.getHash(u.getPassword()));
		}

		userDao.saveOrUpdate(u);

		studentDao.saveOrUpdate(p);

		if (student.getId() == null)
			assignRole(u, ApplicationRoles.STUDENT);

		personDao.saveOrUpdate(student);

	}

	public void removeStudentUser(Person student) {
		User u = student.getUser();

		u = userDao.get(u.getId());

		if (u.getStatus().equals(UserStatus.NEVER_USED)) {
			studentDao.delete(student.getStudent().getId());
			for (UserRole ur : u.getUserRoles())
				userRoleDao.delete(ur.getId());
			userDao.delete(u.getId());
			personDao.delete(student.getId());
			return;
		}

		u.setStatus(UserStatus.MARK_FOR_DELETE);

		userDao.saveOrUpdate(u);

	}

	public void addAbsolventUser(Person absolvent) throws ServiceException {
		User u = absolvent.getUser();
		Absolvent p = absolvent.getAbsolvent();

		if (u.getId() == null) {
			// verific daca mai exista user-ul
			User mock = new User();
			mock.setLogin(u.getLogin());
			List<User> listaUseriExistenti = userDao.findFiltered(mock);
			if (listaUseriExistenti != null && listaUseriExistenti.size() > 0)
				throw new ServiceException("error.login.existent");
		}
		if (u.getId() == null) {
			u.setStatus(UserStatus.NEVER_USED);
			u.setPassword(CryptoUtils.getHash(u.getPassword()));
		}

		userDao.saveOrUpdate(u);

		absolventDao.saveOrUpdate(p);

		if (absolvent.getId() == null)
			assignRole(u, ApplicationRoles.ABSOLVENT);

		personDao.saveOrUpdate(absolvent);

	}

	public void removeAbsolventUser(Person absolvent) {
		User u = absolvent.getUser();

		u = userDao.get(u.getId());

		if (u.getStatus().equals(UserStatus.NEVER_USED)) {
			absolventDao.delete(absolvent.getAbsolvent().getId());
			for (UserRole ur : u.getUserRoles())
				userRoleDao.delete(ur.getId());
			userDao.delete(u.getId());
			personDao.delete(absolvent.getId());

			return;
		}

		u.setStatus(UserStatus.MARK_FOR_DELETE);

		userDao.saveOrUpdate(u);

	}

	public void changeStatus(User user, UserStatus status) {
		user.setStatus(status);
		userDao.saveOrUpdate(user);
	}

	public void registerAbsolventUser(Person absolvent) throws ServiceException {
		User u = absolvent.getUser();
		Absolvent p = absolvent.getAbsolvent();

		if (u.getId() == null) {
			// verific daca mai exista user-ul
			User mock = new User();
			mock.setLogin(u.getLogin());
			List<User> listaUseriExistenti = userDao.findFiltered(mock);
			if (listaUseriExistenti != null && listaUseriExistenti.size() > 0)
				throw new ServiceException("error.login.existent");
		}

		u.setStatus(UserStatus.REQUEST_ACTIVATE);
		u.setPassword(CryptoUtils.getHash(u.getPassword()));

		userDao.saveOrUpdate(u);

		absolventDao.saveOrUpdate(p);

		if (absolvent.getId() == null)
			assignRole(u, ApplicationRoles.ABSOLVENT);

		personDao.saveOrUpdate(absolvent);

	}

	public void registerProfesorUser(Person professor) throws ServiceException {
		User u = professor.getUser();
		Profesor p = professor.getProfesor();

		if (u.getId() == null) {
			// verific daca mai exista user-ul
			User mock = new User();
			mock.setLogin(u.getLogin());
			List<User> listaUseriExistenti = userDao.findFiltered(mock);
			if (listaUseriExistenti != null && listaUseriExistenti.size() > 0)
				throw new ServiceException("error.login.existent");
		}
		u.setStatus(UserStatus.REQUEST_ACTIVATE);
		u.setPassword(CryptoUtils.getHash(u.getPassword()));

		userDao.saveOrUpdate(u);

		profesorDao.saveOrUpdate(p);

		if (professor.getId() == null)
			assignRole(u, ApplicationRoles.PROFESSOR);

		personDao.saveOrUpdate(professor);

	}

	public void registerStudentUser(Person student) throws ServiceException {
		User u = student.getUser();
		Student p = student.getStudent();

		if (u.getId() == null) {
			// verific daca mai exista user-ul
			User mock = new User();
			mock.setLogin(u.getLogin());
			List<User> listaUseriExistenti = userDao.findFiltered(mock);
			if (listaUseriExistenti != null && listaUseriExistenti.size() > 0)
				throw new ServiceException("error.login.existent");
		}
		u.setStatus(UserStatus.REQUEST_ACTIVATE);
		u.setPassword(CryptoUtils.getHash(u.getPassword()));

		userDao.saveOrUpdate(u);

		studentDao.saveOrUpdate(p);

		if (student.getId() == null)
			assignRole(u, ApplicationRoles.STUDENT);

		personDao.saveOrUpdate(student);

	}

}
